package com.spring.utils.api_v4.service.impl;

import com.spring.utils.api_v4.dao.*;
import com.spring.utils.api_v4.pojo.*;
import com.spring.utils.api_v4.pojo.response.BizException;
import com.spring.utils.api_v4.pojo.response.ResultCode;
import com.spring.utils.api_v4.pojo.salary.DeptWage;
import com.spring.utils.api_v4.pojo.salary.task.DeptWageTask;
import com.spring.utils.api_v4.pojo.salary.task.EmployeeSalaryTask;
import com.spring.utils.api_v4.pojo.salary.EmployeeWage;
import com.spring.utils.api_v4.pojo.salary.task.WageTask;
import com.spring.utils.api_v4.service.ISalaryService;
import lombok.extern.java.Log;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Log
@Transactional
public class SalaryService implements ISalaryService {
    @Autowired private IDeptDao deptDao;
    @Autowired private IUserDao userDao;
    @Autowired private IWageTaskDao wageTaskDao;
    @Autowired private IDeptWageTaskDao deptWageTaskDao;
    @Autowired private IRoleDao roleDao;
    @Autowired private IEmployeeSalaryTaskDao employeeSalaryTaskDao;
    @Autowired private IEmployeeWageDao employeeWageDao;
    @Autowired private IDeptWageDao deptWageDao;

    @Override
    public void deleteTask(int id) {
        Optional<WageTask> op = wageTaskDao.findById(id);
        if(op.isEmpty()) {
            throw new BizException(ResultCode.PARAM_IS_INVALID);
        }
        // 获取部门绩效工资信息
        List<DeptWageTask> dwts = deptWageTaskDao.findAllByTask(op.get());
        // 将部门绩效余额返回部门绩效余额
        dwts.forEach(dwt -> {
            DeptWage dw = dwt.getDept();
            dw.setMeritPay(dw.getMeritPay() - (dwt.getTotalMerit() - dwt.getDisMerit()));
            deptWageDao.save(dw);
        });
        wageTaskDao.delete(op.get());
    }

    @Override
    public List<WageTask> getTasksAll() {
        return getTasksAll(false);
    }

    @Override
    public List<WageTask> getTasksAll(boolean count) {
        List<WageTask> tasks = wageTaskDao.findAll();
        if(count){
            tasks.forEach(task -> {
                task.setDeptTaskCount(deptWageTaskDao.countByTask(task));
                task.setFinishedTaskCount(deptWageTaskDao.countByTaskAndState(task, 2));
            });
            wageTaskDao.saveAll(tasks);
        }
        return tasks;
    }

    @Override
    public boolean changeTaskState(int id, int state) {
        Optional<WageTask> op = wageTaskDao.findById(id);
        if(op.isEmpty()) {
            throw new BizException(ResultCode.PARAM_IS_INVALID);
        }
        WageTask task = op.get();
        // 如果是重置
        if(state == 0) {
            task = resetTask(task);
        }
        // 如果是结束
        else if(state == 2) {
            task = finishTask(task);
        }
        // 如果是发布
        else if(state == 1){
            task = publishTask(task);
        }
        wageTaskDao.save(task);
        return true;
    }

    /**
     * 重置任务
     * @param task
     * @return
     */
    private WageTask resetTask(WageTask task) {
        // 将总任务状态设置为0
        task.setState(0);
        // 循环重置部门任务
        task.getDetpTasks().forEach(dt -> {
            resetDeptWageTask(dt);
        });
        return task;
    }

    @Override
    public DeptWageTask resetDeptWageTask(DeptWageTask dt) {
        // 循环检查用户任务是否生成
        employeeWageDao.findByUserInfoDepartId(dt.getDept().getId()).forEach(ew -> {
            if(!employeeSalaryTaskDao.existsByTaskAndUserInfo(dt, ew.getUserInfo())) {
                EmployeeSalaryTask est = new EmployeeSalaryTask();
                est.setTask(dt);
            }
        });

        // 重新计算绩效余额
        DeptWage dw = dt.getDept();
        dt.setMeritPay(dw.getMeritPay() + dt.getMeritPay());
        dw.setMeritPay(0);
        deptWageDao.save(dw);

        // 重置员工工资信息
        dt.getSalaryList().stream()
            .filter(es -> es.getUserInfo().getRawRole().equals("普通职工"))
            .forEach(s -> {
                s.setMeritPay(0);
                s.setState(0);}
            );

        dt.setState(1);
        // 不允许在列表内部直接修改
        // deptWageTaskDao.save(dt);
        return dt;
    }

    /**
     * 发布任务
     * @param task
     * @return
     */
    private WageTask publishTask(WageTask task) {
        // 修改部门任务状态
        task.getDetpTasks().forEach(dt -> {
            // 获取部门工资余额，并累加到任务总额
            dt.setMeritPay(dt.getDept().getMeritPay());    // 获取部门历史余额
            dt.getDept().setMeritPay(0);    // 设置部门余额为0

            // 修改员工工资任务状态
            dt.getSalaryList().forEach(s->{
                s.setState(1);
            });
            dt.setState(1);
        });
        task.setState(1);
        return task;
    }

    /**
     * 结束任务
     */
    private WageTask finishTask(WageTask task) {
        // 循环每个部门
        task.getDetpTasks().forEach(dt -> {
            // 计算每位员工工资
            dt.getSalaryList().forEach(s -> {
                s.setMonthMerit(s.getMeritPay());
                s.setState(2);
            });
            // 计算部门工资总额
            double deptSum = dt.getSalaryList().stream().mapToDouble(s -> s.getMeritPay()).sum();
            dt.setDisMerit(deptSum);    // 设置已分配工资总额
            double remain = dt.getTotalMerit() + dt.getMeritPay() - dt.getDisMerit();   // 计算工资余额
            // 判断工资是否超额
            if(remain < 0) {
                throw new BizException(ResultCode.SALARY_IS_EXCEEDED);
            }
            dt.getDept().setMeritPay(remain);  // 保存部门工资余额
            dt.setState(2);
        });
        task.setState(2);
        return task;
    }

    @Override
    public void saveWageTask(WageTask wt) {
        // 这里需要重新梳理逻辑
        wt.getDetpTasks().forEach(dt -> {
            // 获取部门信息
            Optional<DeptInfo> op = deptDao.findById(dt.getDept().getId());
            if(op.isEmpty()) {
                throw new BizException(ResultCode.DEPARTMENT_NOT_EXIST);
            }
            DeptInfo dept = op.get();
            // 获取员工工资列表
            List<EmployeeWage> ews = employeeWageDao.findByUserInfoDepartId(dept.getId());
            // 生成员工工资任务
            List<EmployeeSalaryTask> ests = ews.stream().map(ew -> {
                EmployeeSalaryTask est = new EmployeeSalaryTask();
                // 此处要特别注意，一定不要复制id
                BeanUtils.copyProperties(ew, est, new String[]{"id"});
                est.setTask(dt);
                return est;
            }).collect(Collectors.toList());

            dt.setSalaryList(ests); // 保存员工工资任务列表
        });
        wageTaskDao.save(wt);
    }

    @Override
    public List<DeptWageTask> getDeptWageTasks(String userId, int taskId) {
        List<DeptWageTask> tasks = new ArrayList<>();
        Optional<WageTask> tp = wageTaskDao.findById(taskId);
        Optional<UserInfo> op = userDao.findById(userId);

        if(op.isPresent() && tp.isPresent()) {
            // 如果是管理员
            if(roleDao.existsByUserInfoAndName(op.get(), "admin")){
                tasks = deptWageTaskDao.findAllByTask(tp.get());
            }
            // 如果是工资员
            else if(roleDao.existsByUserInfoAndName(op.get(), "clerk")){
                DeptWageTask dwt = deptWageTaskDao.findByTaskAndDeptId(tp.get(), op.get().getDeptId());
                tasks = Collections.singletonList(dwt);
            }
        }
        return tasks;
    }

    @Override
    public WageTask getWageTaskById(int taskId) {
        Optional<WageTask> op = wageTaskDao.findById(taskId);
        if(op.isPresent()) {
            return op.get();
        }
        return null;
    }

    @Override
    public Optional<DeptWageTask> getDeptWageTaskById(String taskId) {
        return deptWageTaskDao.findById(taskId);
    }

    @Override
    public List<EmployeeSalaryTask> getDeptSalaries(String deptTaskId) {
        Optional<DeptWageTask> op = deptWageTaskDao.findById(deptTaskId);
        if(op.isEmpty()) {
            throw new BizException(ResultCode.PARAM_IS_INVALID);
        }
        return employeeSalaryTaskDao.findAllByTask(op.get())
                .stream().filter(es -> es.getUserInfo().getRawRole().equals("普通职工"))
                .collect(Collectors.toList());
    }

    @Override
    public EmployeeSalaryTask getEmployeeSalaryById(String id) {
        Optional<EmployeeSalaryTask> op = employeeSalaryTaskDao.findById(id);
        return op.isEmpty()? null : op.get();
    }

    @Override
    public EmployeeWage getUserWage(String userId) {
        Optional<UserInfo> op = userDao.findById(userId);
        if(op.isEmpty()) {
            throw new BizException(ResultCode.USER_NOT_EXIST);
        }
        EmployeeWage employeeWage = employeeWageDao.findByUserInfo(op.get());
        // 如果工资信息不存在，则初始化
        if(employeeWage == null) {
            employeeWage = new EmployeeWage();
            employeeWage.setUserInfo(op.get());
            employeeWageDao.save(employeeWage);
        }
        return employeeWage;
    }

    @Override
    public EmployeeWage getWageById(String id) {
        Optional<EmployeeWage> op = employeeWageDao.findById(id);
        return op.isPresent() ? op.get() : null;
    }

    @Override
    public void saveWage(EmployeeWage wg) {
        employeeWageDao.save(wg);
    }

    @Override
    public List<EmployeeSalaryTask> getWagesOfTask(WageTask task) {
        return employeeSalaryTaskDao.findAllByTaskTask(task);
    }

    @Override
    public void saveDeptWageTask(DeptWageTask dwt) {
        deptWageTaskDao.save(dwt);
    }

    @Override
    public List<EmployeeSalaryTask> getUnsetSalariesOfTask(int taskId) {
        Optional<WageTask> op = wageTaskDao.findById(taskId);
        if(op.isEmpty()) {
            throw new BizException(ResultCode.PARAM_IS_INVALID);
        }
        return employeeSalaryTaskDao.findAllByTaskTask(op.get()).stream().filter(es->es.getState()!=2).collect(Collectors.toList());
    }

    @Override
    public List<EmployeeSalaryTask> getCadreSalariesOfTask(int taskId) {
        Optional<WageTask> op = wageTaskDao.findById(taskId);
        if(op.isEmpty()) {
            throw new BizException(ResultCode.PARAM_IS_INVALID);
        }
        return employeeSalaryTaskDao.findAllByTaskTask(op.get()).stream().filter(es->es.getUserInfo().getRawRole().equals("干部")).collect(Collectors.toList());
    }

    @Override
    public List<WageTask> getWageTasksOfState(int state) {
        return wageTaskDao.findByState(state);
    }

    @Override
    public int saveEmployeeSalaries(List<EmployeeSalaryTask> ess) {
        employeeSalaryTaskDao.saveAll(ess);
        return ess.size();
    }

    @Override
    public boolean hasTaskInProgress() {
        return wageTaskDao.existsByState(1);
    }

    @Override
    public List<DeptWage> getDeptWages() {
        return deptWageDao.findAll();
    }

    @Override
    public DeptWage getDeptWageById(int id) {
        return deptWageDao.findById(id);
    }

    @Override
    public void saveDeptWage(DeptWage dw) {
        deptWageDao.save(dw);
    }

    @Override
    public void deleteDeptWageById(int id) {
        // 根据编号查询部门工资信息
        DeptWage dw = deptWageDao.findById(id);
        if(dw == null) {
            throw new BizException(ResultCode.PARAM_IS_INVALID);
        }
        // 查询所属部门
        Optional<DeptInfo> op = deptDao.findById(id);
        if(op.isEmpty()) {
            throw new BizException(ResultCode.DEPARTMENT_NOT_EXIST);
        }
        // 查询部门所属员工工资信息列表
        List<EmployeeWage> ews = employeeWageDao.findByUserInfoDepartId(op.get().getId());
        // 删除员工工资列表
        employeeWageDao.deleteAll(ews);
        // 删除部门工资信息
        deptWageDao.delete(dw);
    }

    @Override
    public List<EmployeeWage> getEmployeeWagesOfDept(int deptId) {
        return employeeWageDao.findByUserInfoDepartId(deptId);
    }

    @Override
    public void deleteEmployeeWageById(String id) {
        Optional<EmployeeWage> op = employeeWageDao.findById(id);
        if(op.isEmpty()) {
            throw new BizException(ResultCode.PARAM_IS_INVALID);
        }
        employeeWageDao.delete(op.get());
    }

    @Override
    public List<EmployeeWage> initDeptStaffWages(int deptId) {
        // 查询部门
        Optional<DeptInfo> op = deptDao.findById(deptId);
        if(op.isEmpty()) {
            throw new BizException(ResultCode.DEPARTMENT_NOT_EXIST);
        }
        // 查询部门员工列表
        List<UserInfo> users = userDao.findAllByDepart(op.get());
        // 生成工资信息
        List<EmployeeWage> wages = new ArrayList<>();
        // 循环判断该员工工资信息是否存在
        users.forEach(user -> {
            EmployeeWage wage = employeeWageDao.findByUserInfo(user);
            if(wage == null) {
                wage = new EmployeeWage();
                wage.setUserInfo(user);
                wages.add(wage);
            }
        });
        // 保存工资列表
        employeeWageDao.saveAll(wages);
        return wages;
    }

}
